/*
 * date: 2009-11-23 
 * author: Maciej Smolenski <jezdnia@gmail.com>
 */
#ifndef _RPC_PORTMAP_QUERY_H_
#define _RPC_PORTMAP_QUERY_H_

#include <string>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <sstream>
#include <iostream>
#include "log.hpp"
#include "rpc_ping.hpp"
#include "rpc_error.hpp"
#include "rpc_connection.hpp"
#include "rpc_query.hpp"
#include "rpc_monitor_data.hpp"

namespace rpc_query
{

namespace re=rpc_error;
namespace rm=rpc_monitor;
namespace rconn=rpc_connection;

template <typename CT>
class rpc_portmap_query: public rpc_query<CT>{
protected:
	int m_timeout;
	int m_stimeout;
public:
	rpc_portmap_query(std::string p_host, int p_timeout, int p_stimeout) throw (re::rpc_error);
	void operator()(rm::stdset &set) throw (re::rpc_error);
};

template <typename CT>
rpc_portmap_query<CT>::rpc_portmap_query(std::string p_host, int p_timeout, int p_stimeout) throw (re::rpc_error) : rpc_query<CT>(p_host, PMAPPROG, PMAPVERS), m_timeout(p_timeout), m_stimeout(p_stimeout) {};

template <typename CT>
void rpc_portmap_query<CT>::operator()(rm::stdset &set) throw (re::rpc_error)
{
	struct pmaplist *iter,*head = NULL;
	struct timeval timeout;

	timeout.tv_sec = m_timeout;
	timeout.tv_usec = 0;

	clnt_stat result;
	unsigned long procedure = PMAPPROC_DUMP;
	result = clnt_call(rpc_query<CT>::m_client, procedure, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_pmaplist, (caddr_t) &head, timeout);

	switch(result)
	{
		case RPC_SUCCESS:
			break;
		default:
			std::ostringstream os;
			os << "rpc_portmap clnt_call failed host: " << rpc_query<CT>::m_host << " program: " << rpc_query<CT>::m_program << " version: " << rpc_query<CT>::m_version << "procedure: " << procedure << " error: " << clnt_sperror(rpc_query<CT>::m_client, "");
			throw re::rpc_error(re::make_error_code(result), os.str());
	}

	set.clear();
	for(iter=head; iter!=NULL; iter=iter->pml_next)
	{
		state_t state;
		try{
			switch (iter->pml_map.pm_prot)
			{
			case IPPROTO_TCP:
				{
				rpc_ping<rconn::protocol_to_connection<IPPROTO_TCP>::connection_t > rp(rpc_query<CT>::m_host, iter->pml_map.pm_prog, iter->pml_map.pm_vers, m_stimeout);
				rp(state);
				}
				break;
			case IPPROTO_UDP:
				{
				rpc_ping<rconn::protocol_to_connection<IPPROTO_UDP>::connection_t > rp(rpc_query<CT>::m_host, iter->pml_map.pm_prog, iter->pml_map.pm_vers, m_stimeout);
				rp(state);
				}
				break;
			default:
				throw re::rpc_error(re::make_error_code(RPC_FAILED), "unknown protocol: " + iter->pml_map.pm_prot);
			}
		}
		catch(re::rpc_error &re){ 
			if (re.code().value()==RPC_TIMEDOUT)
				state = state_on;
			else
				{
				syslog(LOG_ERR, "rpc_portmap %s", re.what());
				state = state_off;
				}
		}
		rm::rpc_service_string rpcs(std::string(rpc_query<CT>::m_host), iter->pml_map.pm_prog, iter->pml_map.pm_vers, iter->pml_map.pm_port, iter->pml_map.pm_prot, state);
		set.insert(rpcs);
	}

	if (clnt_freeres(rpc_query<CT>::m_client, (xdrproc_t) xdr_pmaplist, (caddr_t) &head)!=1)
	{
		std::ostringstream os;
		os << "rpc_portmap clnt_freeres failed host: " << rpc_query<CT>::m_host << " program: " << rpc_query<CT>::m_program << " version: " << rpc_query<CT>::m_version << "procedure: " << procedure;
		throw re::rpc_error(re::make_error_code(RPC_FAILED), os.str());
	}
}

}

#endif
